<template>
  <mxp-popup
    ref="popupRef"
    model="bottom"
    :animationEndShowContent="false"
    :showToolbar="options.showToolbar"
    :toolbarOptions="options.toolbarOptions"
    :background="options.background || defThemeColor.fill"
    :offsetBottom="options.offset"
    :zIndex="options.zIndex"
    :maskBackground="options.maskBackground"
    :maskClose="options.maskClose"
    @hide="data.showPicker = false"
    @animationEnd="onAnimationEnd"
  >
    <view style="width: 750rpx">
      <slot name="header"></slot>
      <view v-if="data.showPicker" class="picker-view-box-vessel">
        <view class="picker-view-box" :style="{ width: data.width }">
          <view v-if="options.range" class="subtitle-view">
            <text class="subtitle" :style="{ color: defThemeColor.text2 }">{{ options.subtitle[0] || '开始时间' }}</text>
          </view>
          <picker-view
            id="firstList"
            class="picker-view"
            :style="{ height: `calc(${options.itemHeight} * ${options.showItemCount})`, width: data.width }"
            :indicatorStyle="`height: ${options.itemHeight};line-height: ${options.itemHeight};`"
            :value="data.defaultTimeIndex[0]"
            @change="bindChange"
            @pickstart="pickstart"
            @pickend="pickend"
          >
            <picker-view-column>
              <view v-for="(item, index) in data.firstList.hourArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[0][0] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[0][0] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}时
                </text>
              </view>
            </picker-view-column>
            <picker-view-column v-if="options.level > 1">
              <view v-for="(item, index) in data.firstList.minuteArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[0][1] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[0][1] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}分
                </text>
              </view>
            </picker-view-column>
            <picker-view-column v-if="options.level > 2" key="picker-view-column-2">
              <view v-for="(item, index) in data.firstList.secondArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[0][2] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[0][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}秒
                </text>
              </view>
            </picker-view-column>
          </picker-view>
        </view>
        <view v-if="options.range" class="picker-view-box" :style="{ width: data.width }">
          <view class="subtitle-view">
            <text class="subtitle" :style="{ color: defThemeColor.text2 }">{{ options.subtitle[1] || '结束时间' }}</text>
          </view>
          <picker-view
            id="secondList"
            class="picker-view"
            :style="{ width: data.width, minHeight: `200px` }"
            indicatorStyle="height: 40px;line-height: 40px;"
            :value="data.defaultTimeIndex[1]"
            @change="bindChange"
            @pickstart="pickstart"
            @pickend="pickend"
          >
            <picker-view-column>
              <view v-for="(item, index) in data.secondList.hourArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[1][0] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[1][0] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}时
                </text>
              </view>
            </picker-view-column>
            <picker-view-column v-if="options.level > 1">
              <view v-for="(item, index) in data.secondList.minuteArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[1][1] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[1][1] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}分
                </text>
              </view>
            </picker-view-column>
            <picker-view-column v-if="options.level > 2" key="picker-view-column-2">
              <view v-for="(item, index) in data.secondList.secondArr" :key="index" class="picker-item" :style="{ height: options.itemHeight }">
                <text
                  :style="{
                    fontSize: index === data.defaultTimeIndex[1][2] ? options.activeFontSize : options.fontSize,
                    color: index === data.defaultTimeIndex[1][2] ? options.activeColor || defThemeColor.text : options.color || defThemeColor.text2,
                  }"
                >
                  {{ item }}秒
                </text>
              </view>
            </picker-view-column>
          </picker-view>
        </view>
      </view>
      <slot name="bottom"></slot>
    </view>
  </mxp-popup>
</template>

<script setup lang="ts">
  import MxpPopup from '../mxp-popup/mxp-popup.vue';
  import { reactive, ref } from 'vue';
  import type { $ToolbarProps } from '../mxp-toolbar/mxp-toolbar.vue';
  import type { $AllColorStr, $SizeUnitStr } from '../mxp-tool/types/interface';
  import { removeObjectEmpty } from '../mxp-tool/utils/obj';
  import type { PickerViewOnChangeEvent } from 'mxp-uni-types';
  import { defThemeColor } from '../mxp-tool/theme/theme';

  export type $PickerTimeProps = {
    /** 背景颜色 */
    background?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 文本的颜色 */
    color?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的文本颜色 */
    activeColor?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 默认文字的大小 */
    fontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 选中的默认文字的大小 */
    activeFontSize?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 偏移量 （如自定义底部的tabbar就需要一个偏移量） */
    offset?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 层级， 1代表仅选择年,2代表选择年和月, 3代表选择年月日 */
    level?: 1 | 2 | 3; // eslint-disable-line vue/no-unused-properties
    /** 是否为 范围选择 （开始和结束时间） */
    range?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 最小日期 */
    minTime?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 最大日期 */
    maxTime?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 默认日期 */
    defaultTime?: (Date | number | string)[] | Date | number | string; // eslint-disable-line vue/no-unused-properties
    /** 是否留出安全区域 */
    safeSrea?: boolean; // eslint-disable-line vue/no-unused-properties
    /** zIndex */
    zIndex?: number; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    title?: string; // eslint-disable-line vue/no-unused-properties
    /** 标题 */
    subtitle?: [string, string]; // eslint-disable-line vue/no-unused-properties
    /** 是否显示顶部工具栏 */
    showToolbar?: boolean; // eslint-disable-line vue/no-unused-properties
    /** 顶部工具栏配置项 */
    toolbarOptions?: $ToolbarProps; // eslint-disable-line vue/no-unused-properties
    /** 每列显示多少排 */
    showItemCount?: number; // eslint-disable-line vue/no-unused-properties
    /** 单个选项的高度 */
    itemHeight?: $SizeUnitStr; // eslint-disable-line vue/no-unused-properties
    /** 遮罩的背景色 */
    maskBackground?: $AllColorStr; // eslint-disable-line vue/no-unused-properties
    /** 是否可以点击遮罩关闭 */
    maskClose?: boolean; // eslint-disable-line vue/no-unused-properties
  };
  export type $CallBack = {
    confirm?: (time: any) => void;
    change?: (data: { selected: any; type?: 'max' | 'min'; levelIndex: number; index: number; value: string }) => void;
  };
  export type $Options = $CallBack & $PickerTimeProps;
  defineOptions({
    name: 'mxp-picker-time',
  });
  const props = withDefaults(defineProps<$PickerTimeProps>(), {
    background: undefined as $AllColorStr | undefined,
    offset: '0px',
    level: 3,
    range: false,
    minTime: () => ['00:00:00', '00:00:00'],
    maxTime: () => ['23:59:59', '23:59:59'],
    defaultTime: () => ['12:30:30', '12:30:30'],
    safeSrea: true,
    zIndex: 888,
    title: '请选择时间',
    subtitle: () => ['开始时间', '结束时间'],
    toolbarOptions: () => ({}),
    showToolbar: true,
    color: undefined as $AllColorStr | undefined,
    activeColor: undefined as $AllColorStr | undefined,
    fontSize: '14px',
    activeFontSize: '16px',
    showItemCount: 7,
    itemHeight: '44px',
    maskBackground: undefined as $AllColorStr | undefined,
    maskClose: undefined as boolean | undefined,
  });
  defineSlots<{
    /** 自定义顶部内容 */
    header: () => void;
    /** 自定义底部内容 */
    bottom: () => void;
  }>();
  const popupRef = ref<InstanceType<typeof MxpPopup> | null>(null);

  type $Data = {
    width: $SizeUnitStr;
    showPicker: boolean;
    isSubmit: boolean;
    firstList: { hourArr: string[]; minuteArr: string[]; secondArr: string[] };
    secondList: { hourArr: string[]; minuteArr: string[]; secondArr: string[] };
    minTimeNew: Date[];
    maxTimeNew: Date[];
    defaultTimeNew: Date[];
    defaultTimeIndex: [number[], number[]];
  };
  const options = ref<$CallBack & Required<$PickerTimeProps>>(JSON.parse(JSON.stringify(props)));

  const data: $Data = reactive({
    width: '750rpx',
    showPicker: false,
    isSubmit: true,
    firstList: { hourArr: [], minuteArr: [], secondArr: [] },
    secondList: { hourArr: [], minuteArr: [], secondArr: [] },
    minTimeNew: [],
    maxTimeNew: [],
    defaultTimeNew: [],
    defaultTimeIndex: [
      [0, 0, 0],
      [0, 0, 0],
    ],
  });

  /** 显示 */
  function show(param: $Options = {}) {
    options.value = { ...props, ...removeObjectEmpty(param) };
    options.value.toolbarOptions.title = options.value.title;
    init();
    initData();
    popupRef.value?.show({
      confirm() {
        confirm();
      },
    });
    data.showPicker = false;
    setTimeout(() => {
      data.showPicker = true;
    }, 10);
  }
  /** 监听弹出框动画完成 */
  function onAnimationEnd(type: 'hide-animation' | 'show-animation') {
    if (type === 'show-animation') {
      data.showPicker = true;
    } else {
      data.showPicker = false;
    }
  }

  /** 隐藏 */
  function hide() {
    popupRef.value?.hide();
  }

  /**
   * 更新日期 (外部调用此方法可更新日期)
   * @param date - 日期字符串或日期字符串数组
   */
  function undate(date: (Date | number)[] | Date | number) {
    initData(date);
  }

  /** 提交 */
  function confirm() {
    if (data.isSubmit) {
      const arr = [];
      arr.push(getSubmitDateFun(data.firstList, data.defaultTimeIndex[0], options.value.level));
      if (options.value.range) {
        // true 返回数组 false返回字符串
        arr.push(getSubmitDateFun(data.secondList, data.defaultTimeIndex[1], options.value.level));
        if (new Date(`2023/07/17 ${arr[0]}`).getTime() > new Date(`2023/07/17 ${arr[1]}`).getTime()) {
          console.log('开始时间不能大于结束时间');
        } else {
          typeof options.value.confirm === 'function' && options.value.confirm(JSON.parse(JSON.stringify(arr)));
          hide();
        }
      } else {
        typeof options.value.confirm === 'function' && options.value.confirm(arr[0]!);
        hide();
      }
    }
  }
  /** 初始化 */
  function init() {
    data.showPicker = false;
    data.minTimeNew = [];
    data.maxTimeNew = [];
    data.defaultTimeNew = [];
    data.firstList = { hourArr: [], minuteArr: [], secondArr: [] };
    data.secondList = { hourArr: [], minuteArr: [], secondArr: [] };
    data.defaultTimeIndex = [
      [0, 0, 0],
      [0, 0, 0],
    ];
  }
  /** 初始化数据 */
  function initData(time = options.value.defaultTime) {
    data.width = options.value.range ? '350rpx' : '750rpx';

    const defaultTimeNew = Array.isArray(time) ? [...time] : ([time] as (Date | number | string)[]);
    const minTimeNew = Array.isArray(options.value.minTime) ? options.value.minTime : ([options.value.minTime] as (Date | number | string)[]);
    const maxTimeNew = Array.isArray(options.value.maxTime) ? options.value.maxTime : ([options.value.maxTime] as (Date | number | string)[]);

    data.minTimeNew[0] = getStringDate(minTimeNew[0]!, 'start');
    data.maxTimeNew[0] = getStringDate(maxTimeNew[0]!, 'end');
    data.defaultTimeNew[0] = getStringDate(defaultTimeNew[0]!, 'default');
    trmAllDate(0);
    data.firstList = getTimeArr(0, options.value.level!);
    data.defaultTimeIndex[0] = getTimeListIndex(data.firstList, data.defaultTimeNew[0]);
    if (options.value.range) {
      data.minTimeNew[1] = getStringDate(data.minTimeNew[1] || data.minTimeNew[0], 'start');
      data.maxTimeNew[1] = getStringDate(data.maxTimeNew[1] || data.maxTimeNew[0], 'end');
      data.defaultTimeNew[1] = getStringDate(data.defaultTimeNew[1] || data.defaultTimeNew[0], 'default');
      trmAllDate(1);
      data.secondList = getTimeArr(1, options.value.level!);
      data.defaultTimeIndex[0] = getTimeListIndex(data.secondList, data.defaultTimeNew[1]);
    }
  }

  /** 滚轮改变 */
  function bindChange(e: PickerViewOnChangeEvent) {
    const val = e.detail.value;
    const id = e.target!.id as 'firstList' | 'secondList';
    const i = id === 'firstList' ? 0 : 1;
    // 得到是那一列的滚轮发生变化的索引值
    const levelIndex = val.findIndex((item, idx) => item !== (data.defaultTimeIndex[i][idx] || 0));
    const changeIndex = val[levelIndex]!;
    data.defaultTimeIndex[i] = val;

    const keyObj = { 0: 'hourArr', 1: 'minuteArr', 2: 'secondArr' };
    const key = keyObj[levelIndex as keyof typeof keyObj] as 'hourArr' | 'minuteArr' | 'secondArr';
    const ksrq = getSubmitDateFun(data[id], data.defaultTimeIndex[0], options.value.level);
    if (options.value.range) {
      const jsrq = getSubmitDateFun(data[id], data.defaultTimeIndex[1], options.value.level);
      const type: 'max' | 'min' = id === 'firstList' ? 'min' : 'max';
      const res = { selected: [ksrq, jsrq], levelIndex, type, index: changeIndex, value: data[id][key][changeIndex]! };
      typeof options.value.change === 'function' && options.value.change(res);
    } else {
      const res = { selected: ksrq, levelIndex, index: changeIndex, value: data[id][key][changeIndex]! };
      typeof options.value.change === 'function' && options.value.change(res);
    }
  }

  /** 滚动开始 */
  function pickstart() {
    data.isSubmit = false;
  }

  /** 滚动结束 */
  function pickend() {
    data.isSubmit = true;
  }

  /** 日期处理（容错）（遇到非法日期则转换成默认日期 */
  function getStringDate(dateStr: Date | number | string, type: 'default' | 'end' | 'start'): Date {
    let date = dateStr;
    if (typeof date === 'string') {
      const isOk = /^([0-1]?[0-9]|2[0-3])((:([0-5][0-9]))?(:([0-5][0-9])))?$/.test(date);
      if (!isOk) {
        date = type === 'start' ? '2023/02/12 00:00:00' : type === 'end' ? '2023/02/12 23:59:59' : '2023/02/12 12:30:30';
      } else {
        date = `2023/02/12 ${date}`;
      }
    }
    date = new Date(date);
    if (isNaN(date.getHours()) || isNaN(date.getMinutes()) || isNaN(date.getSeconds())) {
      date = new Date(type === 'start' ? '2023/02/12 00:00:00' : type === 'end' ? '2023/02/12 23:59:59' : '2023/02/12 12:30:30');
      if (dateStr) {
        console.error(`传入${type === 'start' ? '最小' : type === 'end' ? '最大' : '默认'}时间不合法!`);
      }
    }
    return date;
  }

  /** 得到时分秒滚轮的数组 */
  function getTimeArr(i: 0 | 1, level: 1 | 2 | 3) {
    const min = data.minTimeNew[i]!;
    const max = data.maxTimeNew[i]!;
    return {
      hourArr: createTimeArray(min.getHours(), max.getHours()),
      minuteArr: level > 1 ? createTimeArray(min.getMinutes(), max.getMinutes()) : [],
      secondArr: level > 2 ? createTimeArray(min.getSeconds(), max.getSeconds()) : [],
    };
  }

  /** 整理时间 */
  function trmAllDate(i: 0 | 1) {
    const min = data.minTimeNew[i]!;
    const max = data.maxTimeNew[i]!;
    const def = data.defaultTimeNew[i]!;
    if (min.getTime() > max.getTime()) {
      data.minTimeNew[i] = max;
      data.maxTimeNew[i] = min;
      console.error(`传入最小时间大于最大时间，建议改正(本组件会自动以交换最大和最小时间)`);
    }
    if (data.minTimeNew[i]!.getTime() > def.getTime()) {
      data.minTimeNew[i] = def;
      console.error(`传入最小时间小于默认时间，建议改正(本组件会自动以自动把默认时间设为最小时间)`);
    }
    if (data.maxTimeNew[i]!.getTime() < def.getTime()) {
      data.maxTimeNew[i] = def;
      console.error(`传入最大时间小于默认时间，建议改正(本组件会自动以自动把默认时间设为最大时间)`);
    }
  }

  /** 得到时间对应的滚轮的下标 */
  function getTimeListIndex(listObj: { hourArr: string[]; minuteArr: string[]; secondArr: string[] }, time: Date) {
    const hour = listObj.hourArr.indexOf(getdoubleDigit(time.getHours()));
    const minute = listObj.minuteArr.indexOf(getdoubleDigit(time.getMinutes()));
    const second = listObj.secondArr.indexOf(getdoubleDigit(time.getSeconds()));
    return [hour > 0 ? hour : 0, minute > 0 ? minute : 0, second > 0 ? second : 0];
  }

  /**
   * 得到最终需要返回的时间
   */
  function getSubmitDateFun(listObj: { hourArr: string[]; minuteArr: string[]; secondArr: string[] }, indexArr: number[], level: 1 | 2 | 3): string {
    const hour = listObj.hourArr[indexArr[0]!]!;
    const minute = indexArr.length > 1 ? listObj.minuteArr[indexArr[1]!] : '00';
    const second = indexArr.length > 2 ? listObj.secondArr[indexArr[2]!] : '00';
    const selectDate = level === 1 ? hour : level === 2 ? `${hour}:${minute}` : `${hour}:${minute}:${second}`;
    return selectDate;
  }

  /**
   * 得到需要的滚轮数组
   */
  function createTimeArray(min: number, max: number): string[] {
    const list = [];
    for (let i = min; i <= max; i++) {
      list.push(getdoubleDigit(i));
    }
    return list;
  }

  /** 得到两位数 */
  function getdoubleDigit(numb: number): string {
    return numb < 10 ? `0${numb}` : `${numb}`;
  }

  defineExpose({
    /** 显示 */
    show,
    /** 隐藏 */
    hide,
    /** 更新日期 */
    undate,
    /** 提交 */
    confirm,
  });
</script>

<style lang="scss" scoped>
  .picker-view-box-vessel {
    display: flex;
    flex-flow: row nowrap;
    justify-content: center;
    width: 750rpx;

    .picker-view-box {
      padding-top: 10px;
      padding-bottom: 20px;

      .subtitle-view {
        display: flex;
        flex-flow: row nowrap;
        align-items: center;
        justify-content: center;
        margin-bottom: 10px;

        .subtitle {
          font-size: 13px;
        }
      }

      .picker-view {
        height: 200px;

        .picker-item {
          display: flex;
          flex-flow: column nowrap;
          align-items: center;
          justify-content: center;
          height: 40px;
          font-size: 13px;
          line-height: 40px;
          text-align: center;
        }
      }
    }
  }
</style>
